from __future__ import annotations

import json
import os
from pathlib import Path
from typing import Any, Optional

from pydantic import BaseModel, Field, ValidationError, field_validator


class MigratorConfig(BaseModel):
    # IO
    input_sql_path: Path
    output_sql_path: Optional[Path] = None

    # Connection (used later by execution layer; validated for shape only here)
    host: Optional[str] = None
    port: Optional[int] = None
    user: Optional[str] = None
    password: Optional[str] = None
    database: Optional[str] = None

    # Behavior
    on_error: str = Field(default="stop", pattern=r"^(stop|skip)$")
    dialect_read: str = "mysql"

    @field_validator("input_sql_path")
    @classmethod
    def _check_input_exists(cls, v: Path) -> Path:
        if not v.exists() or not v.is_file():
            raise ValueError(f"Input file not found: {v}")
        return v


def _load_config_file(path: Optional[str]) -> dict[str, Any]:
    if not path:
        return {}
    p = Path(path)
    if not p.exists():
        raise FileNotFoundError(p)
    # Read config file using streaming (though config files are typically small, this ensures consistency)
    chunks = []
    with open(p, 'r', encoding='utf-8') as f:
        chunk_size = 1024 * 1024  # 1MB chunks
        while True:
            chunk = f.read(chunk_size)
            if not chunk:
                break
            chunks.append(chunk)
    text = ''.join(chunks)
    # Very small loader: accept JSON; YAML can be added later to avoid dependency
    try:
        return json.loads(text)
    except json.JSONDecodeError:
        # Try naive YAML (key: value) lines without nesting
        result: dict[str, Any] = {}
        for line in text.splitlines():
            if not line.strip() or line.strip().startswith("#"):
                continue
            if ":" in line:
                k, v = line.split(":", 1)
                result[k.strip()] = v.strip().strip("'\"")
        return result


def build_config_from_sources(
    *, cli_args: dict[str, Any], env: Optional[dict[str, str]], config_path: Optional[str]
) -> MigratorConfig:
    cfg_file = _load_config_file(config_path)

    def pick_env(key: str, default: Any = None) -> Any:
        if not env:
            return default
        return env.get(key, default)

    merged: dict[str, Any] = {
        # defaults
        "on_error": "stop",
        "dialect_read": "mysql",
        # env
        "host": pick_env("KWDB_HOST"),
        "port": pick_env("KWDB_PORT"),
        "user": pick_env("KWDB_USER"),
        "password": pick_env("KWDB_PASSWORD"),
        "database": pick_env("KWDB_DATABASE"),
    }
    merged.update(cfg_file)
    merged.update(
        {
            "input_sql_path": Path(cli_args.get("file")),
            "output_sql_path": Path(cli_args.get("out")) if cli_args.get("out") else None,
            "host": cli_args.get("host") or merged.get("host"),
            "port": cli_args.get("port") or merged.get("port"),
            "user": cli_args.get("user") or merged.get("user"),
            "password": cli_args.get("password") or merged.get("password"),
            "database": cli_args.get("database") or merged.get("database"),
            "on_error": cli_args.get("on_error") or merged.get("on_error"),
            "dialect_read": cli_args.get("dialect_read") or merged.get("dialect_read"),
        }
    )

    try:
        return MigratorConfig(**merged)
    except ValidationError as e:
        raise SystemExit(f"Configuration invalid: {e}")


